'''

'''
import socket
from threading import Thread
from pymysql import *
import hashlib
import re
import time
import struct
import json


class Mysql():  # 定义数据库类及操作函数
    def __init__(self):
        self.conn = connect(host='xx.xx.xx.xx', port=3306, user='idamini', password='xxxx', database='test')#xx位置填充自己信息
        self.cur = self.conn.cursor()

    def user_list(self):##查询user表的所有用户名
        self.cur.execute('select u_name from user;')
        return self.cur.fetchall()

    def user_id(self,user_name):#根据用户性查询用户id
        self.cur.execute('select u_id from user where u_name= %s;', user_name)
        return self.cur.fetchall()

    def check_id(self,u_id):#检查是否存在查询id的记录，若存在返回1.无返回0.
        self.cur.execute('select count(*) from user where u_id = %s;',u_id)
        return self.cur.fetchall()

    def user_name(self,u_id):#根据id查询用户名
        self.cur.execute('select u_name from user where u_id= %s;', u_id)
        return self.cur.fetchall()

    def friend_tumple(self,user_name):#返回某用户所有好友 元组
        self.cur.execute('select u_name from user,%s where user.u_id = %s.u_id;' % (user_name,user_name))
        return self.cur.fetchall()

    def friends_list(self, user_name):##返回某用户在线朋友 元组
        # select u_name from user,%s, where user.u_id = %s.u_id and user.state = 'on';,user_name,user_name
        self.cur.execute('select u_name from user,%s where user.u_id = %s.u_id and user.state = "on";' % (user_name,user_name))
        return self.cur.fetchall()

    def password(self, user_name):##查询某用户密码。
        self.cur.execute('select u_password from user where u_name = %s;', user_name)
        return self.cur.fetchone()

    def add_user(self, user_name, user_pwd):##添加用户并为该用户设置一个表
        try:
            self.cur.execute('insert into user(u_name,u_password)values(%s,%s);', (user_name, user_pwd))
            ysn = self.cur.execute("create table if not exists %s(fr_id int unsigned auto_increment,u_id int unsigned,"
                                   "primary key(fr_id,u_id))default charset=utf8;"% user_name)
            self.conn.commit()#提交
            return ysn
        except Exception as exc:
            print(exc)

    def add_friends(self, user_name1, user_name2):##添加用户1和用户2为好友
        # insert into 张三(u_id)values((select u_id from user where u_name = '李四'));
        try:
            self.cur.execute('select u_id from user where u_name =  %s ;',user_name2)
            s1=self.cur.fetchone()[0]#fetchone()返回的是元组。[0]取出元组第一个数据
            self.cur.execute('insert into %s (u_id)values(%s);'%(user_name1, s1))
            self.cur.execute('select u_id from user where u_name =  %s ;', user_name1)
            s2 = self.cur.fetchone()[0]
            self.cur.execute('insert into %s (u_id)values(%s);' % (user_name2, s2))
            self.conn.commit()  # 提交
        except Exception as exc:
            print(exc)

    def online_list(self):##查询所有在线用户名
        self.cur.execute("select u_name from user where state = 'on';")
        return self.cur.fetchall()

    def set_on(self, user_name):##设置用户状态为on
        self.cur.execute("update user set state = 'on' where u_name = %s ;", user_name)
        self.conn.commit()  # 提交

    def set_off(self, user_name):#设置用户状态为off
        self.cur.execute("update user set state = 'off' where u_name = %s ;", user_name)
        self.conn.commit()  # 提交

    def __del__(self):  # 执行完自动回收资源
        self.cur.close()
        self.conn.close()


class TcpDeal():
    def __init__(self,socket):
        self.socket = socket
        self.buffer_size=1024

    def send(self, send_message):
        header_dic = {
            "send_data1": len(send_message.encode('utf-8')),
        }
        header_json = json.dumps(header_dic)
        header_bytes = header_json.encode('utf-8')
        # 用struct将报头长度转成固定的4字节长度
        header_size = len(header_bytes)
        self.socket.send(struct.pack('i', header_size))
        # 发送报头
        self.socket.send(header_bytes)
        # 发送数据
        print('发送数据：', bytes(send_message, "utf8"))
        self.socket.send(send_message.encode('utf-8'))

    def receive(self):
        header_size = struct.unpack('i', self.socket.recv(4))[0]
        # 接收报文
        header_bytes = self.socket.recv(header_size)
        # 解析报文
        header_json = header_bytes.decode('utf-8')
        header_dic = json.loads(header_json)
        # 获取真实数据的长度
        data1_len = header_dic['send_data1']
        # 获取数据
        rec_data1 = self.socket.recv(data1_len)
        print('接收数据：', rec_data1.decode('utf-8'))
        return rec_data1.decode('utf-8')



def encrypt_psw(str):  # 使用 MD5 算法对用户的密码进行加密
    hl = hashlib.md5()
    hl.update(str.encode("utf-8"))  # 必须编码后才能加密
    return hl.hexdigest()


socket2user = dict()  # name->socket
socket1user = dict()  # socket->name
mysql = Mysql()


def check_user(username, encrypted_psw):
    # 检查用户登录时输入的用户名和密码是否正确
    # users_list = users_data.split()
    print("开始检查用户信息是否有误")
    try:
        users_list = mysql.user_list()
        if (username,) in users_list:
            print(encrypted_psw)
            if encrypted_psw == mysql.password(user_name=username)[0]:
                print(username)
                mysql.set_on(user_name=username)
                return "登录成功！"
            else:
                return "密码输入有误，请重新输入！"
        else:
            return "不存在该用户，请先注册！"
    except Exception:
            print("发生异常", Exception)


def add_user(new_socket, username, encrypted_psw):  # 添加用户，new_socket: 本次连接的客户端的套接字
    tcpdeal = TcpDeal(socket=new_socket)
    try:
        print('add_user')
        users_list = mysql.user_list()
        if (username,) in users_list:  # 用户名已存在
            tcpdeal.send(send_message="抱歉，用户名已存在！")
            return
        else:
            # 添加用户和用md5加密后的密码
            psw=encrypted_psw
            mysql.add_user(user_name=username, user_pwd=psw)
            tcpdeal.send(send_message="注册成功！")
    except Exception as ret:
        print("添加用户数据出错：" + str(ret))
        tcpdeal.send(send_message="发生未知错误！")


def update():  # 为每个在线用户刷新朋友列表
    for i in mysql.online_list():
        print(i[0])
        update_online_list(i[0])


def update_online_list(username):  # 在线username的朋友列表
    print('')
    tcpdeal = TcpDeal(socket=socket2user[username])
    # 组装所有在线用户名为一个字符串
    print('更新各个用户朋友列表')
    online_usernames = mysql.friends_list(username)
    online = ""
    for sk in online_usernames:
        online += sk[0] + "#!"
    tcpdeal.send(send_message=("#!onlines#!" + online))


def handle_login(new_socket,username_psw):  #处理用户登录
    tcpdeal = TcpDeal(socket=new_socket)
    # 组装后的用户信息格式为 username#!#!password
    ret = re.match(r"(.+)#!#!(.+)", username_psw)
    username = ret.group(1)
    password = ret.group(2)
    encrypted_psw = encrypt_psw(password)
    check_result = check_user(username, encrypted_psw)
    tcpdeal.send(send_message=check_result)
    # 只有登陆成功之后，才执行以下操作
    if check_result == "登录成功！":
        # 将对应的socket与用户名对应起来，并添加到字典中
        print('登陆成功')
        friend_tumple=mysql.friend_tumple(user_name=username)
        friend = " "
        for sk in friend_tumple:
            friend += sk[0] + "&!"
        tcpdeal.send(send_message=("#!friends#!" + friend)) #发送所有朋友名
        socket1user[new_socket] = username
        socket2user[username] = new_socket
        print(socket1user)
        print(socket2user)
        time.sleep(1)
        update()


def handle_reg(new_socket,username_psw):  # 处理客户端的注册请求，接收客户端注册的用户信息
    tcpdeal = TcpDeal(socket=new_socket)
    # 组装后的用户格式为 username#!#!password
    ret = re.match(r"(.+)#!#!(.+)", username_psw)
    username = ret.group(1)
    password = ret.group(2)
    psw = encrypt_psw(password)
    add_user(new_socket, username, psw)


def handle_msg(new_socket,content):  #转发信息
    tcpdeal = TcpDeal(socket=new_socket)
    # 接收组装后的用户格式为 from_username#!#!to_username#!#!message
    ret = re.search(r"(.+)#!#!(.+)#!#!(.+)", content)
    to_username = ret.group(2)
    print('to_username:',to_username)
    if to_username in socket2user:
        tcpdeals = TcpDeal(socket=socket2user[to_username])
        tcpdeals.send(send_message=("#!message#!" + content))
    else:
        tcpdeal.send(send_message=("#!warring#!!对方不在线，请在对方在线时发送！"))


def handle_add_friend(new_socket,content):  #添加好友
    tcpdeal = TcpDeal(socket=new_socket)
    # 接收组装后的用户格式为 ##from_user#!to_user#!ask_add_friend
    ret = re.search(r"(.+)#!#!(.+)", content)
    to_username = ret.group(2)
    print('handle_add_friend content:',content)
    if to_username in socket2user:
        tcpdeals = TcpDeal(socket=socket2user[to_username])
        tcpdeals.send(send_message=("#!Askaddf#!" + content))
    else:
        tcpdeal.send(send_message=("#!warring#!!对方不在线，请在对方在线时发送！"))


def handle_personl_information(new_socket,user):#查询个人信息
    tcpdeal = TcpDeal(socket=new_socket)
    # 接收组装后的用户格式为 username
    print('username:', user)
    (uid,) = mysql.user_id(user_name=user)
    print('uid:',uid)
    tcpdeal.send(send_message=("#!Personl#!"+ str(uid[0]) ))


def handle_id(new_socket,id):
    tcpdeal = TcpDeal(socket=new_socket)
    print ('id:',id)
    print(type(int(id)))
    (exists,) = mysql.check_id(u_id=int(id))
    print(exists)
    if exists[0] == 0:
        tcpdeal.send(send_message=("#!warring#!!无此ID用户"))
    else:
        (uname,) = mysql.user_name(u_id=int(id))
        tcpdeal.send(send_message=("#!Usernam#!" + str(uname[0])))

def deal_add_friend(new_socket, req_con):#处理用户a用户B双方互相添加好友
    ret = re.search(r"(.+)#!#!(.+)#!#!(.+)", req_con)
    from_user = ret.group(1)
    to_username = ret.group(2)
    print(from_user,to_username)
    mysql.add_friends(from_user,to_username)
    tcpdeal = TcpDeal(socket=new_socket)
    tcpdea2 = TcpDeal(socket=socket2user[from_user])
    mess_1 ="添加"+from_user+'为好友成功！！'
    mess_2 ="添加"+to_username+'为好友成功！！'
    tcpdeal.send(send_message=("#!Agreeee#!" + mess_1))
    tcpdea2.send(send_message=("#!Agreeee#!" + mess_2))
    update_online_list(username=from_user)  #添加完好友更新朋友列表
    update_online_list(username=to_username)

def handle(new_socket, addr):  # 服务器运行的主框架，new_socket: 本次连接的客户端套接字，addr: 本次连接客户端的ip和port
    mysql = Mysql()
    tcpdeal = TcpDeal(socket=new_socket)
    try:
        while True:
            receive= tcpdeal.receive()
            ret = re.search(r"(.)(.+)", receive)
            req_type = ret.group(1)
            req_con = ret.group(2)
            if req_type:  # 如果不为真，则说明客户端已断开
                if req_type == "1":  # 登录请求
                    print("开始处理登录请求")
                    handle_login(new_socket,req_con)
                    print('处理登陆请求完毕')
                elif req_type == "2":  # 注册请求
                    print("开始处理注册请求")
                    handle_reg(new_socket,req_con)
                elif req_type == "3":  # 发送消息
                    print("开始处理发送消息请求")
                    handle_msg(new_socket,req_con)
                elif req_type == "4":  # 添加好友
                    print ("开始添加好友")
                    handle_add_friend(new_socket,req_con)
                elif req_type == "5":  # 查询个人信息
                    print("开始查询个人信息")
                    handle_personl_information(new_socket,req_con)
                elif req_type == "6": #根据id查用户名
                    print("开始查询用户名")
                    handle_id(new_socket, req_con)
                elif req_type == "7": #同意用户A和用户B添加好友
                    print("开始处理添加好友请求")
                    deal_add_friend(new_socket, req_con)
            else:
                break
    except Exception as ret:
        print(str(addr) + " 连接异常，准备断开: " + str(ret))
    finally:
        try:
            # 客户端断开后执行的操作
            new_socket.close()
            mysql.set_off(user_name=socket1user[new_socket])
            socket2user.pop(socket1user[new_socket])
            socket1user.pop(new_socket)
            update()  # 各自刷新自己列表
        except Exception as ret:
            print(str(addr) + "连接关闭异常")


if __name__ == "__main__":  # 入口
        try:
            main_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            main_socket.bind(('xx.xx.xx.xx', 9999))  # 服务器绑定的ip和port
            main_socket.listen(128)  # 最大挂起数
            print("服务器启动成功，开始监听...")
            while True:
                new_socket, addr = main_socket.accept()
                Thread(target=handle, args=(new_socket, addr)).start()
        except Exception as ret:
            print("服务器出错: " + str(ret))

